#来源：本人原创
import numpy as np

auxArray = np.zeros((1, 22), dtype=int)
for i in range(22):
    auxArray[0, i] = 2 ** i


def function(x: np.array):
    return 10 * np.sin(5 * x) + 7 * np.abs(x - 5) + 10


class Population():  # 种群
    def __init__(self, chromNum: int, chromBits: int):
        self.chromNum = chromNum
        self.chromBits = chromBits
        self.chromosomeList = np.zeros((chromNum, chromBits))
        self.valueList = np.zeros((1, chromNum))  # 第一行是值大小
        self.xList = np.zeros((1, chromNum))
        self.crossingProbability = 0.75
        self.mutationProbability = 0.01
        self.maxgeneration = 20000
        self.initChromosomes()
        self.xBest = 0
        self.valBest = 0
        self.chroBest = np.zeros((1, chromBits))
        self.calcValue()
        self.run()

    def run(self):
        steps = 0

        while (steps <= self.maxgeneration):

            self.calcValue()
            self.crossing()
            self.mutation()
            self.calcValue()
            self.selection()

            if (steps % 100 == 0):
                # print(self.valueList, self.xList)
                a = np.argmax(self.valueList)
                print(steps, a, self.valBest,self.xBest, self.xList[0,a])
            steps += 1
        a = np.argmax(self.valueList)
        print(np.max(self.valueList), self.xList[0,a])

    def crossing(self):  # 交叉互换
        randArray = np.random.randint(low=0, high=self.chromNum,
                                      size=(1, int(self.crossingProbability * self.chromNum) - 1))
        maxIndex = self.valueList[0].argmax()

        maxRow = np.copy(self.chromosomeList[maxIndex, :])
        bits = np.random.randint(0, self.chromBits)
        tmpList = np.copy(self.chromosomeList)
        for i in range(len(randArray[0, :])):
            tmpRow = np.copy(tmpList[i, :bits])
            tmpList[i, :bits] = self.chromosomeList[randArray[0, i], :bits]
            tmpList[randArray[0, i], :bits] = tmpRow
        m1 = maxIndex
        tmpList[self.chromNum - 1, :] = maxRow
        self.chromosomeList = tmpList

        self.calcValue()
        maxIndex = self.valueList[0].argmax()
        tmpList = np.copy(self.chromosomeList[self.chromNum - 1, :])
        self.chromosomeList[self.chromNum - 1, :] = self.chromosomeList[maxIndex, :]
        self.chromosomeList[maxIndex, :] = tmpList
        self.calcValue()
        maxIndex2 = self.valueList[0].argmax()
        if (self.valBest < self.valueList[0, maxIndex2]):
            self.valBest = self.valueList[0, maxIndex2]
            self.chroBest = self.chromosomeList[maxIndex2,0]
            self.xBest = self.xList[0, maxIndex2]

    def mutation(self):  # 突变
        for j in range(self.chromBits):
            for i in range(self.chromNum - 1):  # 最后一行保留的是最优个体，不进行突变！
                if (np.random.random() < self.mutationProbability):
                    self.chromosomeList[i, j] = self.change(self.chromosomeList[i, j])

    def change(self, var: int):
        if (abs(var - 0) < 0.1):
            return 1
        else:
            return 0

        # self.chromosomeList += np.random.randint(low=0, high=2, size=(self.chromNum, self.chromBits))

    def selection(self):  # 自然选择
        probabilityArray = self.valueList / np.sum(self.valueList)

        choosed = np.random.choice(np.linspace(1, self.chromNum, self.chromNum), size=self.chromNum, replace=True,
                                   p=probabilityArray[0])
        choosed = choosed.astype(int)

        tmpList = np.zeros((self.chromNum, self.chromBits))
        for i in range(self.chromNum):
            tmpList[i, :] = self.chromosomeList[choosed[i] - 1, :]
        self.chromosomeList = tmpList

    def initChromosomes(self):
        self.chromosomeList = np.random.randint(low=0, high=2, size=(self.chromNum, self.chromBits))

    def calcValue(self):
        global auxArray
        r = self.chromosomeList * auxArray
        self.valueList[0] = function(10 * (np.sum(r, axis=1) / (2 ** 22)))
        self.xList[0,:] = 10 * (np.sum(r, axis=1) / (2 ** 22))


if __name__ == "__main__":
    p = Population(100, 22)
